using System;
using System.Collections;
using System.Collections.Generic;
using Main.AStar;
using Main.Details;
using Main.Event;
using Main.GameTime;
using Main.Map;
using Main.Save;
using Main.Transition;
using UnityEngine;
using ZSW.Framework;

namespace Main.NPC
{
    [RequireComponent(typeof(Rigidbody2D))]
    [RequireComponent(typeof(Animator))]
    public class NPCMovement : ZSWF_InitMonoBehaviour, ISaveable
    {
        public ScheduleDataList_SO scheduleData;
        private SortedSet<ScheduleDetails> scheduleSet;
        private ScheduleDetails currentSchedule;

        public bool interactable;
        // 临时存储信息
        [SerializeField] private string currentScene;
        private string targetScene;
        private Vector3Int currentGridPos;
        private Vector3Int nextGridPos;
        private Vector3Int targetGridPos;
        private Vector3 nextWorldPos;
        private Grid CurrentGrid;

        public string StartScene { get => currentScene; set => currentScene = value; }

        public string GUID => GetComponent<DataGUID>().guid;

        [Header("移动属性")]
        public float normalSpeed = 2f;
        private float minSpeed = 1f;
        private float maxSpeed = 10f;

        private Vector2 dir;
        public bool isMoving;

        private bool sceneLoaded;

        [Header("NPC组件")]
        [SerializeField] private Rigidbody2D rb;
        [SerializeField] private SpriteRenderer spriteRenderer;
        [SerializeField] private BoxCollider2D coll;
        [SerializeField] private Animator animator;

        private Stack<MovementStep> movementSteps;

        private bool isInitialized;
        private bool npcMove;

        private float animationBreakTime;
        private bool canPlayStopAnimation;
        private AnimationClip stopAnimationClip;
        public AnimationClip blankAnimationClip;
        private AnimatorOverrideController animatorOverride;

        private bool isFirstLoad;
        public override void Init()
        {
            base.Init();

            movementSteps = new Stack<MovementStep>();
            scheduleSet = new SortedSet<ScheduleDetails>();

            MainEventSystem.BeforeLoadSceneEvent += OnBeforeLoadScene;
            MainEventSystem.AfterLoadSceneEvent += OnAfterLoadScene;
            MainEventSystem.GameDayEvent += OnGameDayChange;

            animatorOverride = new AnimatorOverrideController(animator.runtimeAnimatorController);

            animator.runtimeAnimatorController = animatorOverride;

            foreach (var schedule in scheduleData.ScheduleList)
            {
                scheduleSet.Add(schedule);
            }

            ISaveable saveable = this;
            saveable.RegisterSaveable();

        }

        private void OnDisable()
        {
            MainEventSystem.BeforeLoadSceneEvent -= OnBeforeLoadScene;
            MainEventSystem.AfterLoadSceneEvent -= OnAfterLoadScene;
            MainEventSystem.GameDayEvent -= OnGameDayChange;
        }

        #region 事件回调

        private void OnGameDayChange(int second, int minute, int hour, int day, Season season)
        {
            int time = hour * 100 + minute;

            ScheduleDetails matchSchedule = null;
            foreach (var item in scheduleSet)
            {
                if (item.Time == time)
                {
                    if (item.day != day || item.season != season || item.day == 0)
                    {
                        continue;
                    }
                    matchSchedule = item;
                }
                // 尚未到达当前遍历的行程的时间时
                else if (item.Time > time)
                {
                    break;
                }
            }
            if (matchSchedule != null)
            {
                BuildPath(matchSchedule);
            }
        }

        private void OnBeforeLoadScene()
        {
            sceneLoaded = false;
        }

        public void OnAfterLoadScene()
        {
            sceneLoaded = true;
            CheckVisible();
            CurrentGrid = FindObjectOfType<Grid>();
            if (!isInitialized)
            {
                isInitialized = true;
                InitNPC();
            }

            if (!isFirstLoad)
            {
                isFirstLoad = true;
                currentGridPos = CurrentGrid.WorldToCell(transform.position);
                var schedule = new ScheduleDetails(0, 0, 0, 0, TimeManager.Season, targetScene, (Vector2Int)targetGridPos, stopAnimationClip, interactable);
                BuildPath(schedule);
            }
        }

        #endregion

        private void Update()
        {
            if (sceneLoaded)
            {
                SwitchAnimation();
            }

            animationBreakTime -= Time.deltaTime;
            canPlayStopAnimation = animationBreakTime < 0;
        }


        private void FixedUpdate()
        {
            if (sceneLoaded)
            {
                Movement();
            }
        }



        private void InitNPC()
        {
            targetScene = SceneTransitionManager.CurrentScene;

            currentGridPos = CurrentGrid.WorldToCell(transform.position);
            transform.position = new Vector3(currentGridPos.x + Settings.GridCellSize / 2f, currentGridPos.y + Settings.GridCellSize / 2f, 0);
            targetGridPos = currentGridPos;
        }

        private void Movement()
        {
            if (npcMove)
            {
                return;
            }

            if (movementSteps.Count > 0)
            {
                MovementStep step = movementSteps.Pop();

                currentScene = step.sceneName;

                CheckVisible();

                nextGridPos = (Vector3Int)step.gridCoordinate;

                TimeSpan stepTime = step.GetTimeSpan();

                MoveToGridPosition(nextGridPos, stepTime);
            }
            else if (!isMoving && canPlayStopAnimation)
            {
                StartCoroutine(SetStopAnimation());
            }
        }

        private void MoveToGridPosition(Vector3Int gridPos, TimeSpan stepTime)
        {
            StartCoroutine(MoveRoutine(gridPos, stepTime));
        }

        private IEnumerator MoveRoutine(Vector3Int gridPos, TimeSpan stepTime)
        {
            npcMove = true;
            nextWorldPos = GetWorldPosition(gridPos);
            // 需要移动的步数时间大于游戏进行时间
            if (stepTime > TimeManager.GameTime)
            {
                // 移动时间差(s)
                float timeToMove = (float)(stepTime.TotalSeconds - TimeManager.GameTime.TotalSeconds);
                // 需要移动的距离
                float distance = Vector3.Distance(transform.position, nextWorldPos);
                // 实际移动的速度
                float speed = Mathf.Max(distance / timeToMove / Settings.secondThreshold, minSpeed);

                if (speed <= maxSpeed)
                {
                    while (Vector3.Distance(transform.position, nextWorldPos) > Settings.pixelSize)
                    {
                        // 移动方向
                        dir = (nextWorldPos - transform.position).normalized;

                        Vector2 posOffset = new Vector2(dir.x * speed * Time.fixedDeltaTime, dir.y * speed * Time.fixedDeltaTime);

                        rb.MovePosition(rb.position + posOffset);

                        yield return new WaitForFixedUpdate();
                    }
                }
            }
            rb.position = nextWorldPos;
            currentGridPos = gridPos;
            nextGridPos = currentGridPos;

            npcMove = false;
        }

        /// <summary>
        /// 获取网格中心点的世界坐标
        /// </summary>
        /// <param name="gridPos"></param>
        /// <returns></returns>
        private Vector3 GetWorldPosition(Vector3Int gridPos)
        {
            Vector3 worldPos = CurrentGrid.CellToWorld(gridPos);

            return new Vector3(worldPos.x + Settings.GridCellSize / 2f, worldPos.y + Settings.GridCellSize / 2f);

        }

        #region  切换NPC显示隐藏

        public void CheckVisible()
        {
            if (currentScene == SceneTransitionManager.CurrentScene)
            {
                SetActiveInScene();
            }
            else
            {
                SetDeactiveInScene();
            }
        }


        /// <summary>
        /// 显示NPC
        /// </summary>
        private void SetActiveInScene()
        {
            spriteRenderer.enabled = true;
            coll.enabled = true;
            // transform.GetChild(0).gameObject.SetActive(true);
        }

        /// <summary>
        /// 隐藏NPC
        /// </summary>
        private void SetDeactiveInScene()
        {
            spriteRenderer.enabled = false;
            coll.enabled = false;
            // transform.GetChild(0).gameObject.SetActive(false);
        }
        #endregion


        /// <summary>
        /// 生成路径
        /// </summary>
        /// <param name="schedule"></param>
        public void BuildPath(ScheduleDetails schedule)
        {
            movementSteps.Clear();
            currentSchedule = schedule;
            targetScene = schedule.targetScene;
            targetGridPos = (Vector3Int)schedule.targetGridPosition;
            stopAnimationClip = schedule.clipAtStop;
            this.interactable = schedule.interactable;

            if (schedule.targetScene == currentScene)
            {
                ZSWF_IOCContainer.Get<AStarController>().BuildPath(currentScene, (Vector2Int)currentGridPos, schedule.targetGridPosition, movementSteps);
            }
            else
            {
                //TODO: 跨场景寻路
                if (ZSWF_IOCContainer.Get<NPCManager>().TryGetSceneRoute(currentScene, schedule.targetScene, out SceneRoute sceneRoute))
                {
                    for (int i = 0; i < sceneRoute.scenePathList.Count; i++)
                    {
                        Vector2Int fromPos;
                        Vector2Int gotoPos;
                        ScenePath path = sceneRoute.scenePathList[i];

                        if (path.fromGridCell.x > Settings.maxGridSize)
                        {
                            fromPos = (Vector2Int)currentGridPos;
                        }
                        else
                        {
                            fromPos = path.fromGridCell;
                        }

                        if (path.gotoGridCell.x > Settings.maxGridSize)
                        {
                            gotoPos = (Vector2Int)targetGridPos;
                        }
                        else
                        {
                            gotoPos = path.gotoGridCell;
                        }

                        ZSWF_IOCContainer.Get<AStarController>().BuildPath(path.sceneName, fromPos, gotoPos, movementSteps);
                    }
                }
            }

            // 构建后的路径栈大于1时
            if (movementSteps.Count > 1)
            {
                // 更新对应每一步的时间戳
                UpdateTimeOnPath();
            }
        }

        private void UpdateTimeOnPath()
        {
            MovementStep previousStep = null;

            TimeSpan currentGameTime = TimeManager.GameTime;

            foreach (var step in movementSteps)
            {
                if (previousStep == null)
                {
                    previousStep = step;
                }

                step.hour = currentGameTime.Hours;
                step.minute = currentGameTime.Minutes;
                step.second = currentGameTime.Seconds;

                // 移动到下一步位置所需要的时间
                TimeSpan nextStepTime;

                if (previousStep.CompareIsDiagonal(step))
                {
                    nextStepTime = new TimeSpan(0, 0, (int)(Settings.GridCellDiagonalSize / normalSpeed / Settings.secondThreshold));
                }
                else
                {
                    nextStepTime = new TimeSpan(0, 0, (int)(Settings.GridCellSize / normalSpeed / Settings.secondThreshold));
                }

                currentGameTime = currentGameTime.Add(nextStepTime);

                previousStep = step;
            }


        }

        private void SwitchAnimation()
        {
            isMoving = transform.position != GetWorldPosition(targetGridPos);

            animator.SetBool("IsMoving", isMoving);
            if (isMoving)
            {
                animator.SetBool("Exit", true);
                animator.SetFloat("DirX", dir.x);
                animator.SetFloat("DirY", dir.y);
            }
            else
            {
                animator.SetBool("Exit", false);
            }
        }

        private IEnumerator SetStopAnimation()
        {
            // 修改朝向
            animator.SetFloat("DirX", 0);
            animator.SetFloat("DirY", -1);

            animationBreakTime = Settings.animationBreakTime;

            if (stopAnimationClip != null)
            {
                animatorOverride[blankAnimationClip] = stopAnimationClip;
                animator.SetBool("EventAnimation", true);
                yield return null;
                animator.SetBool("EventAnimation", false);
            }
            else
            {
                animatorOverride[stopAnimationClip] = blankAnimationClip;
                animator.SetBool("EventAnimation", false);
            }

        }

        public GameSaveData GenerateSaveData()
        {
            var data = new GameSaveData();
            data.CharacterPosDict = new Dictionary<string, SerializableVector3>
            {
                { "targetGridPosition", new SerializableVector3(targetGridPos) },
                { "currentGridPosition", new SerializableVector3(transform.position) }
            };
            data.DataSceneName = currentScene;
            data.targetScene = targetScene;

            if (stopAnimationClip != null)
            {
                data.animationInstanceID = stopAnimationClip.GetInstanceID();
            }
            data.interactable = interactable;
            return data;
        }

        public void RestoreData(GameSaveData data)
        {
            var pos = data.CharacterPosDict["targetGridPosition"].ToVector3();
            var gridPos = data.CharacterPosDict["currentGridPosition"].ToVector2Int();
            transform.position = pos;
            targetGridPos = (Vector3Int)gridPos;

            currentScene = data.DataSceneName;
            targetScene = data.targetScene;

            if (data.animationInstanceID != 0)
            {
                stopAnimationClip = Resources.Load<AnimationClip>(data.animationInstanceID.ToString());
            }
            interactable = data.interactable;
        }
    }
}
